﻿//-----------------------------------------------------------------------
// <copyright file="CustomRollingFileLogger.cs" company="SLWY Enterprises">
// * Copyright (C) 2017 四川商旅无忧科技有限公司 版权所有。
// * version : 1.0
// * author  : qinchaoyue
// * FileName: CustomRollingFileLogger.cs
// * history : created by qinchaoyue 2017-11-29 10:23:49
// </copyright>
//-----------------------------------------------------------------------

using System;
using System.Collections.Generic;

namespace Slwy.Logger.Implements.Log4Net
{
    using System.Collections.Concurrent;
    using System.IO;

    using log4net;
    using log4net.Appender;
    using log4net.Core;
    using log4net.Layout;
    using log4net.Repository;
    using log4net.Repository.Hierarchy;

    using Microsoft.Extensions.Options;

    using Slwy.Logger.Models;
    using Slwy.Utility.ConfigurManager;
    using Slwy.Utility.DebugLog;
    using Slwy.Utility.Extensions;

    /// <summary>
    /// 自定义日志输出参数
    /// </summary>
    public class CustomRollingFileLogger
    {
        /// <summary>
        /// The level.
        /// </summary>
        private const string LEVEL = "debug";

        /// <summary>
        /// The ma x_ siz e_ rol l_ backups.
        /// </summary>
        private const int MAX_SIZE_ROLL_BACKUPS = 20;

        /// <summary>
        /// The layou t_ pattern.
        /// </summary>
        private const string LAYOUT_PATTERN = "%m%n";

        /// <summary>
        /// The dat e_ pattern.
        /// </summary>
        private const string DATE_PATTERN = "yyMMddHHmm\".log\"";

        /// <summary>
        /// The logger container.
        /// </summary>
        private static readonly ConcurrentDictionary<string, ILog> loggerContainer = new ConcurrentDictionary<string, ILog>();

        /// <summary>
        /// The appender container.
        /// </summary>
        private static readonly Dictionary<string, ReadParamAppender> appenderContainer = new Dictionary<string, ReadParamAppender>();

        /// <summary>
        /// The lock obj.
        /// </summary>
        private static object lockObj = new object();

        /// <summary>
        /// The maximu m_ fil e_ size.
        /// </summary>
        private readonly string MaximumFileSize;

        /// <summary>
        /// The log folder.
        /// 日志文件输入路径
        /// </summary>
        private readonly string _logFolder;

        /// <summary>
        /// The _log options.
        /// </summary>
        private readonly IOptions<LogOptions> _logOptions;

        /// <summary>
        /// 手动创建 Repository
        /// </summary>
        /// <param name="logOptions">
        /// The log Options.
        /// </param>
        public CustomRollingFileLogger(IOptions<LogOptions> logOptions)
        {
            this._logOptions = logOptions;
            this.MaximumFileSize = this._logOptions.Value.MaximumFileSize;
            this._logFolder = this._logOptions.Value.File;
            //// var configHalder = new ConfigurHandler();
            //// MaximumFileSize = configHalder.GetAppOptionValue("Logging", "MaximumFileSize", "10MB");
            //// var path = AppDomain.CurrentDomain.BaseDirectory;
            //// LogFolder = configHalder.GetAppOptionValue("Logging", "File", path + "\\Logs");
            LogManager.CreateRepository("slwy", typeof(Hierarchy));
        }

        /// <summary>
        /// 获取 logger
        /// </summary>
        /// <param name="loggerName"> 服务名称 </param>
        /// <param name="additivity"> 是否需要输出到控制台 </param>
        /// <returns> The <see cref="ILog"/>.  </returns>
        public ILog GetCustomLogger(string loggerName, bool additivity = false)
        {
            return loggerContainer.GetOrAdd(
                loggerName,
                delegate
                    {
                        // RollingFileAppender newAppender = null;
                        RollingOverMinuteFileAppender newAppender = null;
                        ReadParamAppender appender = null;
                        lock (lockObj)
                        {
                            if (appenderContainer.ContainsKey(loggerName))
                            {
                                appender = appenderContainer[loggerName];
                                newAppender = this.GetNewFileApender(
                                    loggerName,
                                    string.IsNullOrEmpty(appender.File) ? this.GetFile(loggerName) : appender.File,
                                    appender.MaxSizeRollBackups,
                                    appender.AppendToFile,
                                    true,
                                    appender.MaximumFileSize,
                                    RollingFileAppender.RollingMode.Date,
                                    appender.DatePattern,
                                    appender.LayoutPattern);
                            }
                            else
                            {
                                newAppender = this.GetNewFileApender(
                                    loggerName,
                                    this.GetFile(loggerName),
                                    MAX_SIZE_ROLL_BACKUPS,
                                    true,
                                    false,
                                    this.MaximumFileSize,
                                    RollingFileAppender.RollingMode.Date,
                                    DATE_PATTERN,
                                    LAYOUT_PATTERN);
                            }
                        }

                        var repository = (Hierarchy)LogManager.GetRepository("slwy");
                        var logger = repository.LoggerFactory.CreateLogger(repository, loggerName);
                        logger.Hierarchy = repository;
                        logger.Parent = repository.Root;
                        logger.Level = this.GetLoggerLevel(appender == null ? LEVEL : appender.Level);
                        logger.Additivity = additivity;
                        logger.AddAppender(newAppender);
                        logger.Repository.Configured = true;
                        return new LogImpl(logger);
                    });
        }

        /// <summary>
        /// 获取日志文件保存路径
        /// </summary>
        /// <param name="loggerName"> The logger name.  </param>
        /// <returns> The <see cref="string"/>.  </returns>
        private string GetFile(string loggerName)
        {
            try
            {
                var config = new ConfigurHandler();
                var file = config.GetAppOptionValue("Logging", "File", "/var/logs/slwy");
                var serviceName = config.GetAppOptionValue<string>("ServiceInfo", "Name");
                if (serviceName.IsNullOrEmpty())
                {
                    serviceName = "UnknownServiceNamePleaseSetServiceInfoName";
                }

                return Path.Combine(file, loggerName, DateTime.Now.ToString("yyyyMMdd"), serviceName, loggerName);
                //// return $"{file}\\{loggerName}\\{DateTime.Now:yyyyMMdd}\\{serviceName}\\{loggerName}";
            }
            catch (Exception e)
            {
                Logs.Err(e.ToString());
                throw;
            }
        }

        /// <summary>
        /// 获取日志级别
        /// </summary>
        /// <param name="level">日志级别</param>
        /// <returns>日志级别</returns>
        private Level GetLoggerLevel(string level)
        {
            if (!string.IsNullOrEmpty(level))
            {
                switch (level.ToLower().Trim())
                {
                    case "debug":
                        return Level.Debug;

                    case "info":
                        return Level.Info;

                    case "warn":
                        return Level.Warn;

                    case "error":
                        return Level.Error;

                    case "fatal":
                        return Level.Fatal;
                }
            }

            return Level.Debug;
        }

        /// <summary>
        /// 创建日志配置
        /// </summary>
        /// <param name="appenderName"> The appender name.  </param>
        /// <param name="file"> The file.  </param>
        /// <param name="maxSizeRollBackups"> The max size roll backups.  </param>
        /// <param name="appendToFile"> The append to file.  </param>
        /// <param name="staticLogFileName"> The static log file name.  </param>
        /// <param name="maximumFileSize"> The maximum file size.  </param>
        /// <param name="rollingMode"> The rolling mode.  </param>
        /// <param name="datePattern"> The date pattern.  </param>
        /// <param name="layoutPattern"> The layout pattern.  </param>
        /// <returns> The <see cref="RollingFileAppender"/>.  </returns>
        private RollingOverMinuteFileAppender GetNewFileApender(string appenderName, string file, int maxSizeRollBackups, bool appendToFile = true, bool staticLogFileName = false, string maximumFileSize = "5MB", RollingFileAppender.RollingMode rollingMode = RollingFileAppender.RollingMode.Composite, string datePattern = "yyyyMMdd\".txt\"", string layoutPattern = "%m%n")
        {
            RollingOverMinuteFileAppender appender = new RollingOverMinuteFileAppender(this._logOptions.Value.StatisticsTimeInterval)
            {
                LockingModel = new FileAppender.MinimalLock(),
                Name = appenderName,
                File = file,
                AppendToFile = appendToFile,

                //// MaxSizeRollBackups = maxSizeRollBackups,
                
                MaximumFileSize = maximumFileSize,
                StaticLogFileName = staticLogFileName,
                RollingStyle = rollingMode,
                DatePattern = datePattern,
            };
            PatternLayout layout = new PatternLayout(layoutPattern);
            appender.Layout = layout;
            layout.ActivateOptions();
            appender.ActivateOptions();
            return appender;
        }
    }
}